﻿using EditoneLib.Annotations;
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;

namespace EditoneLib
{
    public abstract class BaseAudioClip : IClip, IHasStream
    {
        private Stream _stream;

        public BaseAudioClip()
        {
            Stream = new MemoryStream();
        }

        public BaseAudioClip(Stream stream)
        {
            Stream = stream;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public int BlockAlign
        {
            get { return NumChannels*Settings.SampleSize; }
        }

        public int FloatSamplesPerSecond
        {
            get { return NumChannels*Settings.SampleRate; }
        }

        public abstract int NumChannels { get; }

        public Stream Stream
        {
            get { return _stream; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                _stream = value;
                OnPropertyChanged();
            }
        }

        public TimeSpan TotalLength
        {
            get { return TimeSpan.FromTicks(Stream.Length/BlockAlign*10000000/Settings.SampleRate); }
        }

        public virtual float[,] this[TimeSpan start, TimeSpan stop]
        {
            get
            {
                return this[
                    start.Ticks * Settings.SampleRate / 10000000,
                    stop.Ticks * Settings.SampleRate / 10000000
                    ];
            }
        }

        public virtual float[,] this[long start, long stop]
        {
            get
            {
                if (stop < start)
                    throw new ArgumentException("Stop must be greater or equal to start.");

                var blockCount = (int) (stop - start);

                int floatBufferOffset = (start < 0) ? (int) -start : 0;

                int readBytesLength = (int) Math.Min(Stream.Length, blockCount*BlockAlign)
                                      - floatBufferOffset*BlockAlign;

                var buf = new byte[blockCount*BlockAlign];

                var floatBuffer = new float[blockCount, NumChannels];
                
                Stream.Position = start*BlockAlign;

                Stream.Read(buf, floatBufferOffset*BlockAlign, readBytesLength);

                Buffer.BlockCopy(buf, 0, floatBuffer, 0, readBytesLength);

                return floatBuffer;
            }
        }

        public abstract Tuple<BaseAudioClip, BaseAudioClip> Split(TimeSpan offset);

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}